package kgame5.k3d.core.obj3d.node3d.animator {
import kgame5.k3d.core.obj3d.node3d.Mesh3D;
import kgame5.k3d.core.obj3d.node3d.animator.channel.AbstractChannel3D;
import kgame5.k3d.core.obj3d.node3d.animator.channel.MorphChannel3D;
import kgame5.k3d.core.render.RenderSessionData;

import flash.utils.Dictionary;
import flash.utils.getTimer;

/**
 * @author kangkai
 * 动画器，自动加载md2文件
 * !!注意41行，这里是始终不能播放到最后一帧的
 * 
 * !!注意，曾经出过错误，关键帧需要设置不要超过边界
 * 
 * mtab的表结构
 * 
<?xml version="1.0" encoding="UTF-8"?>
<action_table fps="4" A="12" B="30" H="11">
	<action name="stand">
		<frames from="0" to="0"/>
		<from_actions names="end,fire,hurt"/>
		<eoa_command name="LOOP" param=""/>
	</action>
	<action name="start">
		<frames from="9" to="11"/>
		<from_actions names="stand"/>
		<eoa_command name="GOTO" param="run"/>
	</action>
	<action name="run">
		<frames from="1" to="1"/>
		<from_actions names="start"/>
		<eoa_command name="LOOP" param=""/>
	</action>
	<action name="end">
		<frames from="12" to="14"/>
		<from_actions names="run"/>
		<eoa_command name="GOTO" param="stand"/>
	</action>
	<action name="fire">
		<frames from="2" to="4"/>
		<from_actions names="stand,start,run,end"/>
		<eoa_command name="GOTO" param="stand"/>
	</action>
	<action name="hurt">
		<frames from="5" to="8"/>
		<from_actions names="stand,start,run,end,fire,hurt"/>
		<eoa_command name="GOTO" param="stand"/>
	</action>
</action_table>
 */
public class Animator3D extends Mesh3D{
	
	//如果不设置动作表，则有个默认的动作表
	//TODO 如果修改动作表格式，注意修改这个要
	public static const DEFAULT_ACTION_TABLE:XML=
	<action_table fps="4" A="64" B="64" H="64">
		<action name="all">
			<frames from="0" to="-1"/>
			<from_actions names=""/>
			<eoa_command name="LOOP" param=""/>
		</action>
	</action_table>;
	//动作列表,里面是morphChannel3D
	private var actionList:Dictionary;
		
	private var _isPlaying:Boolean = false;
	private var _currentChannel:AbstractChannel3D;
	private var _currentTime:Number = 0;
	
	private var keyframeList:Array;
	private var frameDuration:Number;//一帧持续的时间
	
	private var actionNames:Array=new Array();//就是给外部读取用的
	
	//actionList是名字和MorphChannel3D的一个dinctonar
	public function Animator3D(){
		super();
	}
	
	public function setupKeyframeList(keyframeList:Array):void{
		this.keyframeList=keyframeList;
	}
	
	//动作表
	public function setupActionTableXML(mtab:XML=null):void{
		//trace("setupActionTable");
		if(keyframeList==null){
			throw new Error("先设置关键帧列表吧！");
		}
		
		//如果不设置动作表，则只有一帧
		if(mtab==null){
			mtab=DEFAULT_ACTION_TABLE;
			//throw new Error("还没做好呢");
		}
		
		actionList=new Dictionary();
		frameDuration=1/Number(mtab.@fps);
		this.ti.hardVO.aabb.A=int(mtab.@A);
		this.ti.hardVO.aabb.B=int(mtab.@B);
		this.ti.hardVO.aabb.H=int(mtab.@H);
		
		for each(var actionXML:XML in mtab.action){
			
			var name:String=actionXML.@name;
			var action:MorphChannel3D=new MorphChannel3D(this,name);
			
			//加上关键帧
			var startFrameIdx:int=int(actionXML.frames.@from);
			var endFrameIdx:int=int(actionXML.frames.@to);
			if(endFrameIdx>keyframeList.length-1){
				throw new Error("mtab非法，无此endFrameIdx,请检查mtab.xml frameLength:"+keyframeList.length+"e:"+endFrameIdx);
			}
			if(endFrameIdx==-1){
				endFrameIdx=keyframeList.length-1;
			}
			
			for(var i:int=startFrameIdx;i<=endFrameIdx;i++){
				var frame:KeyFrame3D=keyframeList[i];
				frame.time=(i-startFrameIdx)*frameDuration;
				action.addKeyFrame(frame);
			}
			
			//EOA动作
			action.eoaCommand=actionXML.eoa_command.@name;
			action.eoaCommandParam=actionXML.eoa_command.@param;
			
			//跳转限制
			action.dictFromActionNames[action.name]=1;//表示可以从自己跳到自己
			
			var ss:String=actionXML.from_actions.@names;
			if(ss!=""){
				var fromActionNames:Array=ss.split(",");
				for each(var nn:String in fromActionNames){
					action.dictFromActionNames[nn]=1;
				}
			}
			
			//-加入表中
			actionList[name]=action;
			
			actionNames.push(name);//只是给外部读取用的，没实际意义
		}
		
		//停在第一动作第一帧
		gotoAndStop(actionNames[0]);
	}
	
	//check是说是否检查本跳转的合法性,默认是不执行这个检查的
	//返回的是是否成功跳转了
	public function gotoAndPlay(actionName:String,frameIdx:int=0,check:Boolean=false):Boolean{
		
		//跳转检查
		if(check){
			if(_currentChannel!=null){
				var aimChannel:MorphChannel3D=actionList[actionName];
				if(aimChannel.dictFromActionNames[_currentChannel.name] == null){//不被允许跳转
					//throw new Error("不被允许的动作 当前："+_currentChannel.name+" 目的:"+actionName);
					return false;
				}
			}
		}
		
		_currentChannel = actionList[actionName];
		_currentTime = getTimer()-frameIdx*frameDuration;
		_isPlaying = true;
		
		_currentChannel.updateToFrame(frameIdx);
		
		return true;
	}
	
	public function gotoAndStop(actionName:String,frameIdx:int=0):void{
		_currentChannel = actionList[actionName];
		_currentTime = getTimer()-frameIdx*frameDuration;
		_isPlaying = false;
		
		_currentChannel.updateToFrame(frameIdx);
	}
	
	//默认是播放全体动画,这是是说播放某一动作，从头开始播放
	public function play():void{
		_isPlaying = true;
	}
	
	public function stop():void{
		_isPlaying = false;
	}
	
	override public function Update(rsd:RenderSessionData,isParentDirty:Boolean):void{
		//处理动画
		if(_isPlaying 
			&& _currentChannel!=null
			&&_currentChannel.keyFrames.length>1 //只有一帧则不需要动画了，这个在gotoAndPlay里做了
			){
				//--注意这样才能有动画呢
				this.ti.bForceRefresh=true;
				
				//--处理动画
				var secs:Number = _currentTime / 1000;
				var duration:Number = _currentChannel.duration;
				var elapsed:Number = (getTimer()/1000) - secs;
				var progress:Number;
				//if(elapsed > duration){//##这里原来的
				//##修改之后，则如果要循环动画，则需要起始帧和结束帧相同即可循环动画,最后一帧不会被播放
				//如果恰好到达了最后一帧，则认为该播放的是第一帧
				//曾经出现过错误的原因是当elapse与dur相等的时候，是按0~1的动画做的，当elapse>dur的时候
				//又有一次这样做了，所以导致了跳跃
				
				var bEoa:Boolean=false;//这个是是否动画该结束了
				
				if(elapsed >= duration){
					_currentTime = getTimer();
					elapsed = 0;
					bEoa=true;
				}
				
				if(bEoa){//动画结束
					switch(_currentChannel.eoaCommand){
						case AbstractChannel3D.EOA_COMMAND_GOTO://跳转到某动作
						this.gotoAndPlay(_currentChannel.eoaCommandParam, 0);
						break;
						
						case AbstractChannel3D.EOA_COMMAND_LOOP://循环播放某动作
						 progress= elapsed / duration;
						_currentChannel.updateToTime(progress);
						break;
						
						case AbstractChannel3D.EOA_COMMAND_STOP://停到最后一帧
						this.stop();
						_currentChannel.updateToFrame(_currentChannel.keyFrames.length-1);
						break;
					}
				}else{//动画还没结束呢
					progress = elapsed / duration;
					_currentChannel.updateToTime(progress);
				}
		}
		
		//其他,投影并显示
		super.Update(rsd, isParentDirty);
	}
	
	public function getActionNames():Array{
		return actionNames;
	}
	
	public function getAction(actionName:String):AbstractChannel3D{
		return actionList[actionName];
	}

}//end of class
}

////动作表
//	public function setupActionTable(sTable:String=null):void{
//		trace("setupActionTable");
//		if(keyframeList==null){
//			throw new Error("先设置关键帧列表吧！");
//		}
//		
//		//如果不设置动作表，则只有一帧
//		if(sTable==null){
//			sTable=DEFAULT_ACTION_TABLE;
//		}
//		
//		actionList=new Dictionary();
//		
//		var eoaCommand:String;
//		var eoaCommandParam:String;
//		//#写个解析器forXX
//		var line:Array=Util.ReadTxt(sTable);
//		//trace(line[0]);
//		//trace(line[1]);
//		//trace(line);
//		//提取fps
//		var firstLine:String=line.shift();
//		frameDuration=1/Number((firstLine.split(":"))[1]);
//		
//		//trace("dur:"+frameDuration);
//		
//		//提取各个动作
//		for each(var s:String in line){
//			var aData:Array=s.split(",");
//			
//			var name:String=aData[0];
//			var startFrameIdx:int=int(aData[1]);
//			var endFrameIdx:int=int(aData[2]);
//			eoaCommand=aData[3];
//			eoaCommandParam=aData[4];
//			
//			var action:MorphChannel3D=new MorphChannel3D(this,name);
//			
//			for(var i:int=startFrameIdx;i<=endFrameIdx;i++){
//				var frame:KeyFrame3D=keyframeList[i];
//				frame.time=(i-startFrameIdx)*frameDuration;
//				action.addKeyFrame(frame);
//			}
//			
//			action.eoaCommand=eoaCommand;
//			action.eoaCommandParam=eoaCommandParam;
//			
//			actionList[name]=action;
//			
//			actionNames.push(name);//只是给外部读取用的，没实际意义
//			
//		}
//		
//		//停在第一动作第一帧
//		gotoAndStop(actionNames[0]);
//	}
